package edu.cicese.ans;

import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileFilter;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;

public class Utilities {
	/** Minimum number of interest-points expected to be considered a match.*/
	public static int minPoiMatched = 7;

//	public static String baseDir = "D:\\Pasitos\\ans-tm\\";
	public static String baseDir = "D:\\Tests\\Experimento9991.1\\";
//	public static String baseDir = "D:\\Tests\\Experimento999\\";
//	public static String baseDir = "C:\\ANS2\\";
	public static String spaceDir = baseDir + "spaces\\";
	public static String workDir =  baseDir + "workspace\\";

	public static String locationDir =  workDir + "imgs\\location\\";
	public static String image = workDir + "imgs\\img01.jpg";
	public static String imageForLoc = workDir + "imgs\\img02.jpg";
	public static String[] imageForLocation = new String[]{
			locationDir + "img11.jpg", locationDir + "img12.jpg", locationDir + "img13.jpg" };

	public static final int SERVER_PORT = 8000;
	public static final int CLIENT_PORT = 8001;
	public static int imageNo = 0;
	public static boolean queryOnly = true;
	public static String imagePath = workDir + "imgs\\";
	public static String imageName;
	
//	public static String userLocation = null;
	public static int userLocationMinimumStrength = 20;
	public static float userLocationRatio = 0.75f;
	public static int userLocationStrength = 0;
	public static String[] userLocations;
	public static List<Object[]> filesToUpload;

	public final static float TAG_THRESHOLD = 0.00015f;
	public final static float LOCATION_THRESHOLD = 0.001f;

	public final static String SERVER_LOC_THRESHOLD = "001", SERVER_LOC_RES = "480x360";

	public static boolean loadingPOIs = false, loadingTags = false;
	
	/**
	 * Gets the tag repository changelog and determines which files need to be
	 * uploaded to the client tag repository copy.
	 *
	 * @return a {@code String} with the tag repository changes
	 */
    @SuppressWarnings("unchecked")
	public static String getTreeChanges() {
	    String treeChanges;
	    filesToUpload = new ArrayList<Object[]>();
	    if (new File(spaceDir + "changelog.ans").exists()) {
			treeChanges = loadString(spaceDir + "changelog.ans");
			JSONObject joSpace, joTag;
			JSONArray jaTags, jaChangelog = (JSONArray)JSONValue.parse(treeChanges);

		    if (jaChangelog != null) {
			    String spaceID;
			    for (Object jaChange : jaChangelog) {
				    joSpace = (JSONObject) jaChange;
				    spaceID = joSpace.get("spaceID").toString();
				    if (Integer.parseInt(joSpace.get("action").toString()) != Command.DELETED) {
					    jaTags = (JSONArray) joSpace.get("tags");
					    for (Object jaTag : jaTags) {
						    joTag = (JSONObject) jaTag;
						    if (Integer.parseInt(joTag.get("action").toString()) != Command.DELETED) {
							    addFilesToUpload(spaceID, joTag.get("tagID").toString());
						    }
					    }
				    }
			    }
		    }
		    else {
			    saveString(spaceDir + "changelog.ans", "[]");
			    treeChanges = "[]";
		    }
	    }
	    else {
		    saveString(spaceDir + "changelog.ans", "[]");
		    treeChanges = "[]";
	    }
	    Utilities.print("FILES TO UPLOAD: " + filesToUpload.size());
		return treeChanges;
	}

	@SuppressWarnings({"unchecked"})
	public static String getTree() {
		filesToUpload = new ArrayList<Object[]>();
		JSONArray jaTree = new JSONArray();
		JSONObject joSpace;
		JSONArray jaTags;
		JSONObject joTag;
		File spaceDirectory = new File(spaceDir);
		FileFilter filter = new FileFilter() {
      		public boolean accept(File file) {
          		return file.isDirectory();
      		}
		};
		File[] spaces = spaceDirectory.listFiles(filter);
		File tagDir;
		File[] tags;
		if (spaces != null) {
			for (File space : spaces) {
				tagDir = new File(getTagDir(space.getName()) + "all");
				tags = tagDir.listFiles(filter);
				joSpace = new JSONObject();
				joSpace.put("spaceID", space.getName());
				joSpace.put("action", Command.CREATED);
				jaTags = new JSONArray();
				if (tags != null) {
					for (File tag : tags) {
						joTag = new JSONObject();
						joTag.put("tagID", tag.getName());
						joTag.put("action", Command.CREATED);
						jaTags.add(joTag);
						addFilesToUpload(space.getName(), tag.getName());
					}
				}
				joSpace.put("tags", jaTags);
				jaTree.add(joSpace);
			}
		}

		return jaTree.toJSONString();
	}

	/*public static String getTree() {
		JSONArray tree = new JSONArray();
	    filesToUpload = new ArrayList<Object[]>();
		File dir = new File(spaceDir);
		FileFilter filter = new FileFilter() {
      		public boolean accept(File file) {
          		return file.isDirectory();
      		}
		};
		File[] spaces = dir.listFiles(filter);
		File[] tags;
		for (File space : spaces) {
			tags = new File(space + "\\tags\\all").listFiles(filter);
			if (tags != null) {
				for (File tag : tags) {
					addFilesToUpload(space.getName(), tag.getName());
				}
			}
		}

//	    if (new File(spaceDir + "changelog.ans").exists()) {
//			tree = loadString(spaceDir + "changelog.ans");
//			JSONObject joSpace, joTag;
//			JSONArray jaTags, jaChangelog = (JSONArray)JSONValue.parse(tree);
//
//		    if (jaChangelog != null) {
//			    String spaceID;
//			    for (Object jaChange : jaChangelog) {
//				    joSpace = (JSONObject) jaChange;
//				    spaceID = joSpace.get("spaceID").toString();
//				    if (Integer.parseInt(joSpace.get("action").toString()) != Command.DELETED) {
//					    jaTags = (JSONArray) joSpace.get("tags");
//					    for (Object jaTag : jaTags) {
//						    joTag = (JSONObject) jaTag;
//						    if (Integer.parseInt(joTag.get("action").toString()) != Command.DELETED) {
//							    addFilesToUpload(spaceID, joTag.get("tagID").toString());
//						    }
//					    }
//				    }
//			    }
//		    }
//		    else {
//			    saveString(spaceDir + "changelog.ans", "[]");
//			    tree = "[]";
//		    }
//	    }
//	    else {
//		    saveString(spaceDir + "changelog.ans", "[]");
//		    tree = "[]";
//	    }
//	    Utilities.print("FILES TO UPLOAD: " + filesToUpload.size());
		return tree;
	}*/

	/**
     * Adds repository files needed to be uploaded to the client.
     *
     * @param spaceID the space identifier
     * @param tagID the tag identifier
     */
	public static void addFilesToUpload(String spaceID, String tagID) {
		File tagDir = new File(getTagDir(spaceID) + "all\\" + tagID + "\\");
		FilenameFilter filter = new FilenameFilter() {
      		public boolean accept(File file, String name) {
      			name = name.toLowerCase();
          		return name.endsWith(".txt") || name.endsWith(".png") || name.endsWith(".wav") || name.endsWith(".3gp");
      		}
		};
		File[] files = tagDir.listFiles(filter);
		for (File file : files) {
			try {
				filesToUpload.add(new Object[]{file.getName(), spaceID, file.length(), file.getCanonicalPath()});
			} catch (IOException e) { e.printStackTrace(); }
		}
	}

	/**
	 * Gets the tag directory path for a given space.
	 *
	 * @param spaceID the space identifier
	 * @return the tag directory path for the given space
	 */
	public static String getTagDir(String spaceID) {
		return spaceDir + spaceID + "\\tags\\";
	}

	/**
	 * Loads the IPs/tags repository changelog from a file.
	 *
	 * @param filename the path from the file to load
	 * @return a {@code JSONObject} with the IPs/tags repository changelog
	 */
	public static JSONObject loadJSONObject(String filename) {
		String changelog = loadString(filename);
		if (changelog == null) {
			saveString(filename, new JSONObject().toJSONString());
			return new JSONObject();
		}
		return (JSONObject) JSONValue.parse(changelog);
	}

	/**
	 * Saves a {@code String} into a file.
	 *
	 * @param filename the path where the file is going to be saved
	 * @param string the {@code String} to be saved
	 */
	public static void saveString(String filename, String string) {
		try {
			PrintWriter pw = new PrintWriter(filename);
			pw.print(string);
			pw.close();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Loads a {@code String} from a file.
	 *
	 * @param filename the path from the file to load
	 * @return the {@code String} loaded
	 */
	public static String loadString(String filename) {
		String string = "";
		try {
			BufferedReader br =  new BufferedReader(new FileReader(new File(filename)));
			try {
				String line = br.readLine();
				while (line != null){
					string += line;
					line = br.readLine();
					if (line != null) {
						string += System.getProperty("line.separator");
					}
				}
			}
			finally {
				br.close();
			}
		}
		catch (IOException e){
			Utilities.print("File missing.");
			e.printStackTrace();
			return null;
		}
		return string;
	}

	/**
	 * Checks the needed directories and creates them if necessary
	 */
	@SuppressWarnings({"ResultOfMethodCallIgnored"})
	public static void checkDirs() {
		new File(spaceDir).mkdirs();
		new File(locationDir).mkdirs();
	}

	/**
	 * Removes a file extension.
	 *
	 * @param filename the path from the file to be processed
	 * @return the filename without extension
	 */
    public static String filenameWithoutExtension(String filename) {
	    int dot = filename.lastIndexOf(".");
	    int sep = filename.lastIndexOf(File.separator);
	    return filename.substring(sep + 1, dot);
  	}

	/**
	 * Adds zeros to the left of a {@code int} to reach certain
	 * length.
	 *
	 * @param value the {@code int} value to be modified
	 * @param length the required length for the given value
	 * @return a {@code String} with the added zeros
	 */
	public static String addZeros(int value, int length) {
		String valueWithZeros = String.valueOf(value);
		while (valueWithZeros.length() < length) {
			valueWithZeros = "0" + valueWithZeros;
		}
		return valueWithZeros;
	}

	/**
	 * Prints a message in console with a time stamp.
	 *
	 * @param msg the message to be printed
	 */
	public static void print(String msg) {
		Calendar calendar = Calendar.getInstance();
		System.out.println("[" +
				addZeros(calendar.get(Calendar.HOUR_OF_DAY), 2) + ":" +
				addZeros(calendar.get(Calendar.MINUTE), 2) + ":" +
				addZeros(calendar.get(Calendar.SECOND), 2) + "] " + msg);
	}
}